﻿namespace WebPacker.Packs
{
	using System;
	using WebPacker.Filters;
	using WebPacker.Packs.Resources;
	using WebPacker.Packs.Watchers;
	using WebPacker.VersionGenerators;

	public class Pack : IPack, IDisposable
	{
		private readonly PackType packType;
		private readonly string name;
		private readonly IFilter filter;
		private readonly IResource[] resources;
		private readonly IVersionGenerator generator;
		private readonly IResourceWatcher watcher;
		private string version;
		private bool disposed;

		public Pack(PackType packType, string name, IResource[] resources, IFilter filter)
			: this(packType, name, resources, filter, null, null, null)
		{
		}

		public Pack(PackType packType, string name, IResource[] resources, IFilter filter, IResourceWatcher resourceWatcher, IVersionGenerator versionGenerator, string version)
		{
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}

			if (resources == null)
			{
				throw new ArgumentNullException("resources");
			}

			if (filter != null && filter.PackType != packType)
			{
				throw new ArgumentException("Invalid filter type.");
			}

			this.name = name;
			this.filter = filter;
			this.packType = packType;
			this.resources = resources;
			this.version = version ?? string.Empty;
			this.watcher = resourceWatcher;
			this.generator = versionGenerator;

			if (Monitoring)
			{
				foreach (var resource in resources)
				{
					if (watcher.Accept(resource))
					{
						watcher.Add(resource);
					}
				}

				watcher.Changed += OnResourceChanged;
			}
		}

		public string Name
		{
			get { return name; }
		}

		public PackType PackType
		{
			get { return packType; }
		}

		public IFilter Filter
		{
			get { return filter; }
		}

		public IResource[] Resources
		{
			get { return resources; }
		}

		public string Version
		{
			get { return version; }
		}

		public bool Monitoring
		{
			get { return generator != null && watcher != null; }
		}

		/// <summary>
		/// For test purpose.
		/// </summary>
		internal IVersionGenerator VersionGenerator
		{
			get { return generator; }
		}

		/// <summary>
		/// For test purpose.
		/// </summary>
		internal IResourceWatcher ResourceWatcher
		{
			get { return watcher; }
		}

		public string GetContent()
		{
			var contents = PackHelper.GetContents(resources);

			if (filter != null)
			{
				for (var i = 0; i < contents.Length; i++)
				{
					contents[i] = filter.Apply(contents[i]);
				}
			}

			var content = string.Concat(contents);

			if (filter != null)
			{
				content = filter.ApplyToCombined(content);
			}

			return content;
		}

		public string GetSourceContent()
		{
			return string.Concat(PackHelper.GetContents(resources));
		}

		public DateTime GetLastModifiedTime()
		{
			var maxLastModified = DateTime.MinValue;

			foreach (var resource in Resources)
			{
				var lastModified = resource.GetLastModifiedTime();

				if (lastModified > maxLastModified)
				{
					maxLastModified = lastModified;
				}
			}

			return maxLastModified;
		}

		public void UpdateVersion()
		{
			if (generator != null)
			{
				version = generator.GetNextVersion(this);
			}
		}

		public void Dispose()
		{
			Dispose(true);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing && !disposed)
			{
				if (Monitoring)
				{
					foreach (var resource in resources)
					{
						if (watcher.Accept(resource))
						{
							watcher.Remove(resource);
						}
					}

					watcher.Changed -= OnResourceChanged;
				}

				disposed = true;
			}
		}

		private void OnResourceChanged(object sender, ResourceChangedEventArgs ea)
		{
			foreach (var resource in resources)
			{
				if (resource.Name == ea.ResourceName)
				{
					UpdateVersion();
					break;
				}
			}
		}
	}
}